// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef GPU_COMMAND_BUFFER_SERVICE_TEXTURE_MANAGER_H_
#define GPU_COMMAND_BUFFER_SERVICE_TEXTURE_MANAGER_H_

#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
#include "base/memory/ref_counted.h"
#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/service/gl_utils.h"
#include "gpu/command_buffer/service/memory_tracking.h"
#include "gpu/gpu_export.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gl/gl_image.h"
#include <algorithm>
#include <list>
#include <set>
#include <string>
#include <vector>

namespace gl {
class GLImage;
}

namespace gpu {
namespace gles2 {

    class GLES2Decoder;
    struct ContextState;
    struct DecoderFramebufferState;
    class Display;
    class ErrorState;
    class FeatureInfo;
    class FramebufferManager;
    class MailboxManager;
    class TextureManager;
    class TextureRef;

    // Info about Textures currently in the system.
    // This class wraps a real GL texture, keeping track of its meta-data. It is
    // jointly owned by possibly multiple TextureRef.
    class GPU_EXPORT Texture {
    public:
        explicit Texture(GLuint service_id);

        GLenum min_filter() const
        {
            return min_filter_;
        }

        GLenum mag_filter() const
        {
            return mag_filter_;
        }

        GLenum wrap_r() const
        {
            return wrap_r_;
        }

        GLenum wrap_s() const
        {
            return wrap_s_;
        }

        GLenum wrap_t() const
        {
            return wrap_t_;
        }

        GLenum usage() const
        {
            return usage_;
        }

        GLenum pool() const
        {
            return pool_;
        }

        GLenum compare_func() const
        {
            return compare_func_;
        }

        GLenum compare_mode() const
        {
            return compare_mode_;
        }

        GLfloat max_lod() const
        {
            return max_lod_;
        }

        GLfloat min_lod() const
        {
            return min_lod_;
        }

        GLint base_level() const
        {
            return base_level_;
        }

        GLint max_level() const
        {
            return max_level_;
        }

        int num_uncleared_mips() const
        {
            return num_uncleared_mips_;
        }

        uint32 estimated_size() const
        {
            return estimated_size_;
        }

        bool CanRenderTo() const
        {
            return target_ != GL_TEXTURE_EXTERNAL_OES;
        }

        // The service side OpenGL id of the texture.
        GLuint service_id() const
        {
            return service_id_;
        }

        void SetServiceId(GLuint service_id)
        {
            DCHECK(service_id);
            service_id_ = service_id;
        }

        // Returns the target this texure was first bound to or 0 if it has not
        // been bound. Once a texture is bound to a specific target it can never be
        // bound to a different target.
        GLenum target() const
        {
            return target_;
        }

        bool SafeToRenderFrom() const
        {
            return cleared_;
        }

        // Get the width/height/depth for a particular level. Returns false if level
        // does not exist.
        // |depth| is optional and can be nullptr.
        bool GetLevelSize(
            GLint target, GLint level,
            GLsizei* width, GLsizei* height, GLsizei* depth) const;

        // Get the type of a level. Returns false if level does not exist.
        bool GetLevelType(
            GLint target, GLint level, GLenum* type, GLenum* internal_format) const;

        // Get the image bound to a particular level. Returns NULL if level
        // does not exist.
        gl::GLImage* GetLevelImage(GLint target, GLint level) const;

        bool HasImages() const
        {
            return has_images_;
        }

        // Returns true of the given dimensions are inside the dimensions of the
        // level.
        bool ValidForTexture(
            GLint target,
            GLint level,
            GLint xoffset,
            GLint yoffset,
            GLint zoffset,
            GLsizei width,
            GLsizei height,
            GLsizei depth) const;

        bool IsValid() const
        {
            return !!target();
        }

        bool IsAttachedToFramebuffer() const
        {
            return framebuffer_attachment_count_ != 0;
        }

        void AttachToFramebuffer()
        {
            ++framebuffer_attachment_count_;
        }

        void DetachFromFramebuffer()
        {
            DCHECK_GT(framebuffer_attachment_count_, 0);
            --framebuffer_attachment_count_;
        }

        void SetImmutable(bool immutable)
        {
            immutable_ = immutable;
        }

        bool IsImmutable() const
        {
            return immutable_;
        }

        // Get the cleared rectangle for a particular level. Returns an empty
        // rectangle if level does not exist.
        gfx::Rect GetLevelClearedRect(GLenum target, GLint level) const;

        // Whether a particular level/face is cleared.
        bool IsLevelCleared(GLenum target, GLint level) const;

        // Whether the texture has been defined
        bool IsDefined() const
        {
            return estimated_size() > 0;
        }

        // Initialize TEXTURE_MAX_ANISOTROPY to 1 if we haven't done so yet.
        void InitTextureMaxAnisotropyIfNeeded(GLenum target);

        void OnWillModifyPixels();
        void OnDidModifyPixels();

        void DumpLevelMemory(base::trace_event::ProcessMemoryDump* pmd,
            uint64_t client_tracing_id,
            const std::string& dump_name) const;

    private:
        friend class MailboxManagerImpl;
        friend class MailboxManagerSync;
        friend class MailboxManagerTest;
        friend class TextureDefinition;
        friend class TextureManager;
        friend class TextureRef;
        friend class TextureTestHelper;

        ~Texture();
        void AddTextureRef(TextureRef* ref);
        void RemoveTextureRef(TextureRef* ref, bool have_context);
        MemoryTypeTracker* GetMemTracker();

        // Condition on which this texture is renderable. Can be ONLY_IF_NPOT if it
        // depends on context support for non-power-of-two textures (i.e. will be
        // renderable if NPOT support is in the context, otherwise not, e.g. texture
        // with a NPOT level). ALWAYS means it doesn't depend on context features
        // (e.g. complete POT), NEVER means it's not renderable regardless (e.g.
        // incomplete).
        enum CanRenderCondition {
            CAN_RENDER_ALWAYS,
            CAN_RENDER_NEVER,
            CAN_RENDER_ONLY_IF_NPOT
        };

        struct LevelInfo {
            LevelInfo();
            LevelInfo(const LevelInfo& rhs);
            ~LevelInfo();

            gfx::Rect cleared_rect;
            GLenum target;
            GLint level;
            GLenum internal_format;
            GLsizei width;
            GLsizei height;
            GLsizei depth;
            GLint border;
            GLenum format;
            GLenum type;
            scoped_refptr<gl::GLImage> image;
            uint32 estimated_size;
        };

        struct FaceInfo {
            FaceInfo();
            ~FaceInfo();

            GLsizei num_mip_levels;
            std::vector<LevelInfo> level_infos;
        };

        // Set the info for a particular level.
        void SetLevelInfo(const FeatureInfo* feature_info,
            GLenum target,
            GLint level,
            GLenum internal_format,
            GLsizei width,
            GLsizei height,
            GLsizei depth,
            GLint border,
            GLenum format,
            GLenum type,
            const gfx::Rect& cleared_rect);

        // In GLES2 "texture complete" means it has all required mips for filtering
        // down to a 1x1 pixel texture, they are in the correct order, they are all
        // the same format.
        bool texture_complete() const
        {
            return texture_complete_;
        }

        // In GLES2 "cube complete" means all 6 faces level 0 are defined, all the
        // same format, all the same dimensions and all width = height.
        bool cube_complete() const
        {
            return cube_complete_;
        }

        // Whether or not this texture is a non-power-of-two texture.
        bool npot() const
        {
            return npot_;
        }

        // Marks a |rect| of a particular level as cleared.
        void SetLevelClearedRect(GLenum target,
            GLint level,
            const gfx::Rect& cleared_rect);

        // Marks a particular level as cleared or uncleared.
        void SetLevelCleared(GLenum target, GLint level, bool cleared);

        // Updates the cleared flag for this texture by inspecting all the mips.
        void UpdateCleared();

        // Clears any renderable uncleared levels.
        // Returns false if a GL error was generated.
        bool ClearRenderableLevels(GLES2Decoder* decoder);

        // Clears the level.
        // Returns false if a GL error was generated.
        bool ClearLevel(GLES2Decoder* decoder, GLenum target, GLint level);

        // Sets a texture parameter.
        // TODO(gman): Expand to SetParameteriv,fv
        // Returns GL_NO_ERROR on success. Otherwise the error to generate.
        GLenum SetParameteri(
            const FeatureInfo* feature_info, GLenum pname, GLint param);
        GLenum SetParameterf(
            const FeatureInfo* feature_info, GLenum pname, GLfloat param);

        // Makes each of the mip levels as though they were generated.
        bool MarkMipmapsGenerated(const FeatureInfo* feature_info);

        bool NeedsMips() const
        {
            return min_filter_ != GL_NEAREST && min_filter_ != GL_LINEAR;
        }

        // True if this texture meets all the GLES2 criteria for rendering.
        // See section 3.8.2 of the GLES2 spec.
        bool CanRender(const FeatureInfo* feature_info) const;

        // Returns true if mipmaps can be generated by GL.
        bool CanGenerateMipmaps(const FeatureInfo* feature_info) const;

        // Returns true if any of the texture dimensions are not a power of two.
        static bool TextureIsNPOT(GLsizei width, GLsizei height, GLsizei depth);

        // Returns true if texture face is complete relative to the first face.
        static bool TextureFaceComplete(const Texture::LevelInfo& first_face,
            size_t face_index,
            GLenum target,
            GLenum internal_format,
            GLsizei width,
            GLsizei height,
            GLsizei depth,
            GLenum format,
            GLenum type);

        // Returns true if texture mip level is complete relative to first level.
        static bool TextureMipComplete(const Texture::LevelInfo& level0_face,
            GLenum target,
            GLint level,
            GLenum internal_format,
            GLsizei width,
            GLsizei height,
            GLsizei depth,
            GLenum format,
            GLenum type);

        // Sets the Texture's target
        // Parameters:
        //   target: GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP or
        //           GL_TEXTURE_EXTERNAL_OES or GL_TEXTURE_RECTANGLE_ARB
        //           GL_TEXTURE_2D_ARRAY or GL_TEXTURE_3D (for GLES3)
        //   max_levels: The maximum levels this type of target can have.
        void SetTarget(
            const FeatureInfo* feature_info, GLenum target, GLint max_levels);

        // Update info about this texture.
        void Update(const FeatureInfo* feature_info);

        // Set the image for a particular level.
        void SetLevelImage(
            const FeatureInfo* feature_info,
            GLenum target,
            GLint level,
            gl::GLImage* image);

        // Appends a signature for the given level.
        void AddToSignature(
            const FeatureInfo* feature_info,
            GLenum target, GLint level, std::string* signature) const;

        void SetMailboxManager(MailboxManager* mailbox_manager);

        // Updates the unsafe textures count in all the managers referencing this
        // texture.
        void UpdateSafeToRenderFrom(bool cleared);

        // Updates the uncleared mip count in all the managers referencing this
        // texture.
        void UpdateMipCleared(LevelInfo* info,
            GLsizei width,
            GLsizei height,
            const gfx::Rect& cleared_rect);

        // Computes the CanRenderCondition flag.
        CanRenderCondition GetCanRenderCondition() const;

        // Updates the unrenderable texture count in all the managers referencing this
        // texture.
        void UpdateCanRenderCondition();

        // Updates the images count in all the managers referencing this
        // texture.
        void UpdateHasImages();

        // Increment the framebuffer state change count in all the managers
        // referencing this texture.
        void IncAllFramebufferStateChangeCount();

        MailboxManager* mailbox_manager_;

        // Info about each face and level of texture.
        std::vector<FaceInfo> face_infos_;

        // The texture refs that point to this Texture.
        typedef std::set<TextureRef*> RefSet;
        RefSet refs_;

        // The single TextureRef that accounts for memory for this texture. Must be
        // one of refs_.
        TextureRef* memory_tracking_ref_;

        // The id of the texure
        GLuint service_id_;

        // Whether all renderable mips of this texture have been cleared.
        bool cleared_;

        int num_uncleared_mips_;
        int num_npot_faces_;

        // The target. 0 if unset, otherwise GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP.
        //             Or GL_TEXTURE_2D_ARRAY or GL_TEXTURE_3D (for GLES3).
        GLenum target_;

        // Texture parameters.
        GLenum min_filter_;
        GLenum mag_filter_;
        GLenum wrap_r_;
        GLenum wrap_s_;
        GLenum wrap_t_;
        GLenum usage_;
        GLenum pool_;
        GLenum compare_func_;
        GLenum compare_mode_;
        GLfloat max_lod_;
        GLfloat min_lod_;
        GLint base_level_;
        GLint max_level_;

        // The maximum level that has been set.
        GLint max_level_set_;

        // Whether or not this texture is "texture complete"
        bool texture_complete_;

        // Whether mip levels have changed and should be reverified.
        bool texture_mips_dirty_;
        bool texture_mips_complete_;

        // Whether or not this texture is "cube complete"
        bool cube_complete_;

        // Whether any level 0 faces have changed and should be reverified.
        bool texture_level0_dirty_;
        bool texture_level0_complete_;

        // Whether or not this texture is non-power-of-two
        bool npot_;

        // Whether this texture has ever been bound.
        bool has_been_bound_;

        // The number of framebuffers this texture is attached to.
        int framebuffer_attachment_count_;

        // Whether the texture is immutable and no further changes to the format
        // or dimensions of the texture object can be made.
        bool immutable_;

        // Whether or not this texture has images.
        bool has_images_;

        // Size in bytes this texture is assumed to take in memory.
        uint32 estimated_size_;

        // Cache of the computed CanRenderCondition flag.
        CanRenderCondition can_render_condition_;

        // Whether we have initialized TEXTURE_MAX_ANISOTROPY to 1.
        bool texture_max_anisotropy_initialized_;

        DISALLOW_COPY_AND_ASSIGN(Texture);
    };

    // This class represents a texture in a client context group. It's mostly 1:1
    // with a client id, though it can outlive the client id if it's still bound to
    // a FBO or another context when destroyed.
    // Multiple TextureRef can point to the same texture with cross-context sharing.
    class GPU_EXPORT TextureRef : public base::RefCounted<TextureRef> {
    public:
        TextureRef(TextureManager* manager, GLuint client_id, Texture* texture);
        static scoped_refptr<TextureRef> Create(TextureManager* manager,
            GLuint client_id,
            GLuint service_id);

        void AddObserver() { num_observers_++; }
        void RemoveObserver() { num_observers_--; }

        const Texture* texture() const { return texture_; }
        Texture* texture() { return texture_; }
        GLuint client_id() const { return client_id_; }
        GLuint service_id() const { return texture_->service_id(); }
        GLint num_observers() const { return num_observers_; }

    private:
        friend class base::RefCounted<TextureRef>;
        friend class Texture;
        friend class TextureManager;

        ~TextureRef();
        const TextureManager* manager() const { return manager_; }
        TextureManager* manager() { return manager_; }
        void reset_client_id() { client_id_ = 0; }

        TextureManager* manager_;
        Texture* texture_;
        GLuint client_id_;
        GLint num_observers_;

        DISALLOW_COPY_AND_ASSIGN(TextureRef);
    };

    // Holds data that is per gles2_cmd_decoder, but is related to to the
    // TextureManager.
    struct DecoderTextureState {
        // total_texture_upload_time automatically initialized to 0 in default
        // constructor.
        explicit DecoderTextureState(const FeatureInfo::Workarounds& workarounds)
            : tex_image_failed(false)
            , texture_upload_count(0)
            , texsubimage_faster_than_teximage(
                  workarounds.texsubimage_faster_than_teximage)
            , force_cube_map_positive_x_allocation(
                  workarounds.force_cube_map_positive_x_allocation)
            , force_cube_complete(workarounds.force_cube_complete)
        {
        }

        // This indicates all the following texSubImage*D calls that are part of the
        // failed texImage*D call should be ignored. The client calls have a lock
        // around them, so it will affect only a single texImage*D + texSubImage*D
        // group.
        bool tex_image_failed;

        // Command buffer stats.
        int texture_upload_count;
        base::TimeDelta total_texture_upload_time;

        bool texsubimage_faster_than_teximage;
        bool force_cube_map_positive_x_allocation;
        bool force_cube_complete;
    };

    // This class keeps track of the textures and their sizes so we can do NPOT and
    // texture complete checking.
    //
    // NOTE: To support shared resources an instance of this class will need to be
    // shared by multiple GLES2Decoders.
    class GPU_EXPORT TextureManager : public base::trace_event::MemoryDumpProvider {
    public:
        class GPU_EXPORT DestructionObserver {
        public:
            DestructionObserver();
            virtual ~DestructionObserver();

            // Called in ~TextureManager.
            virtual void OnTextureManagerDestroying(TextureManager* manager) = 0;

            // Called via ~TextureRef.
            virtual void OnTextureRefDestroying(TextureRef* texture) = 0;

        private:
            DISALLOW_COPY_AND_ASSIGN(DestructionObserver);
        };

        enum DefaultAndBlackTextures {
            kTexture2D,
            kTexture3D,
            kTexture2DArray,
            kCubeMap,
            kExternalOES,
            kRectangleARB,
            kNumDefaultTextures
        };

        TextureManager(MemoryTracker* memory_tracker,
            FeatureInfo* feature_info,
            GLsizei max_texture_size,
            GLsizei max_cube_map_texture_size,
            GLsizei max_rectangle_texture_size,
            GLsizei max_3d_texture_size,
            bool use_default_textures);
        ~TextureManager() override;

        void set_framebuffer_manager(FramebufferManager* manager)
        {
            framebuffer_manager_ = manager;
        }

        // Init the texture manager.
        bool Initialize();

        // Must call before destruction.
        void Destroy(bool have_context);

        // Returns the maximum number of levels.
        GLint MaxLevelsForTarget(GLenum target) const
        {
            switch (target) {
            case GL_TEXTURE_2D:
                return max_levels_;
            case GL_TEXTURE_RECTANGLE_ARB:
            case GL_TEXTURE_EXTERNAL_OES:
                return 1;
            case GL_TEXTURE_3D:
            case GL_TEXTURE_2D_ARRAY:
                return max_3d_levels_;
            default:
                return max_cube_map_levels_;
            }
        }

        // Returns the maximum size.
        GLsizei MaxSizeForTarget(GLenum target) const
        {
            switch (target) {
            case GL_TEXTURE_2D:
            case GL_TEXTURE_EXTERNAL_OES:
                return max_texture_size_;
            case GL_TEXTURE_RECTANGLE:
                return max_rectangle_texture_size_;
            case GL_TEXTURE_3D:
            case GL_TEXTURE_2D_ARRAY:
                return max_3d_texture_size_;
            default:
                return max_cube_map_texture_size_;
            }
        }

        // Returns the maxium number of levels a texture of the given size can have.
        static GLsizei ComputeMipMapCount(GLenum target,
            GLsizei width,
            GLsizei height,
            GLsizei depth);

        // Checks if a dimensions are valid for a given target.
        bool ValidForTarget(
            GLenum target, GLint level,
            GLsizei width, GLsizei height, GLsizei depth);

        // True if this texture meets all the GLES2 criteria for rendering.
        // See section 3.8.2 of the GLES2 spec.
        bool CanRender(const TextureRef* ref) const
        {
            return ref->texture()->CanRender(feature_info_.get());
        }

        // Returns true if mipmaps can be generated by GL.
        bool CanGenerateMipmaps(const TextureRef* ref) const
        {
            return ref->texture()->CanGenerateMipmaps(feature_info_.get());
        }

        // Sets the Texture's target
        // Parameters:
        //   target: GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP
        //           GL_TEXTURE_2D_ARRAY or GL_TEXTURE_3D (for GLES3)
        //   max_levels: The maximum levels this type of target can have.
        void SetTarget(
            TextureRef* ref,
            GLenum target);

        // Set the info for a particular level in a TexureInfo.
        void SetLevelInfo(TextureRef* ref,
            GLenum target,
            GLint level,
            GLenum internal_format,
            GLsizei width,
            GLsizei height,
            GLsizei depth,
            GLint border,
            GLenum format,
            GLenum type,
            const gfx::Rect& cleared_rect);

        Texture* Produce(TextureRef* ref);

        // Maps an existing texture into the texture manager, at a given client ID.
        TextureRef* Consume(GLuint client_id, Texture* texture);

        // Sets |rect| of mip as cleared.
        void SetLevelClearedRect(TextureRef* ref,
            GLenum target,
            GLint level,
            const gfx::Rect& cleared_rect);

        // Sets a mip as cleared.
        void SetLevelCleared(TextureRef* ref, GLenum target,
            GLint level, bool cleared);

        // Sets a texture parameter of a Texture
        // Returns GL_NO_ERROR on success. Otherwise the error to generate.
        // TODO(gman): Expand to SetParameteriv,fv
        void SetParameteri(
            const char* function_name, ErrorState* error_state,
            TextureRef* ref, GLenum pname, GLint param);
        void SetParameterf(
            const char* function_name, ErrorState* error_state,
            TextureRef* ref, GLenum pname, GLfloat param);

        // Makes each of the mip levels as though they were generated.
        // Returns false if that's not allowed for the given texture.
        bool MarkMipmapsGenerated(TextureRef* ref);

        // Clears any uncleared renderable levels.
        bool ClearRenderableLevels(GLES2Decoder* decoder, TextureRef* ref);

        // Clear a specific level.
        bool ClearTextureLevel(
            GLES2Decoder* decoder, TextureRef* ref, GLenum target, GLint level);

        // Creates a new texture info.
        TextureRef* CreateTexture(GLuint client_id, GLuint service_id);

        // Gets the texture info for the given texture.
        TextureRef* GetTexture(GLuint client_id) const;

        // Removes a texture info.
        void RemoveTexture(GLuint client_id);

        // Gets a Texture for a given service id (note: it assumes the texture object
        // is still mapped in this TextureManager).
        Texture* GetTextureForServiceId(GLuint service_id) const;

        TextureRef* GetDefaultTextureInfo(GLenum target)
        {
            switch (target) {
            case GL_TEXTURE_2D:
                return default_textures_[kTexture2D].get();
            case GL_TEXTURE_3D:
                return default_textures_[kTexture3D].get();
            case GL_TEXTURE_2D_ARRAY:
                return default_textures_[kTexture2DArray].get();
            case GL_TEXTURE_CUBE_MAP:
                return default_textures_[kCubeMap].get();
            case GL_TEXTURE_EXTERNAL_OES:
                return default_textures_[kExternalOES].get();
            case GL_TEXTURE_RECTANGLE_ARB:
                return default_textures_[kRectangleARB].get();
            default:
                NOTREACHED();
                return NULL;
            }
        }

        bool HaveUnrenderableTextures() const
        {
            return num_unrenderable_textures_ > 0;
        }

        bool HaveUnsafeTextures() const
        {
            return num_unsafe_textures_ > 0;
        }

        bool HaveUnclearedMips() const
        {
            return num_uncleared_mips_ > 0;
        }

        bool HaveImages() const
        {
            return num_images_ > 0;
        }

        GLuint black_texture_id(GLenum target) const
        {
            switch (target) {
            case GL_SAMPLER_2D:
                return black_texture_ids_[kTexture2D];
            case GL_SAMPLER_3D:
                return black_texture_ids_[kTexture3D];
            case GL_SAMPLER_2D_ARRAY:
                return black_texture_ids_[kTexture2DArray];
            case GL_SAMPLER_CUBE:
                return black_texture_ids_[kCubeMap];
            case GL_SAMPLER_EXTERNAL_OES:
                return black_texture_ids_[kExternalOES];
            case GL_SAMPLER_2D_RECT_ARB:
                return black_texture_ids_[kRectangleARB];
            default:
                NOTREACHED();
                return 0;
            }
        }

        size_t mem_represented() const
        {
            return memory_tracker_managed_->GetMemRepresented() + memory_tracker_unmanaged_->GetMemRepresented();
        }

        void SetLevelImage(
            TextureRef* ref,
            GLenum target,
            GLint level,
            gl::GLImage* image);

        size_t GetSignatureSize() const;

        void AddToSignature(
            TextureRef* ref,
            GLenum target,
            GLint level,
            std::string* signature) const;

        void AddObserver(DestructionObserver* observer)
        {
            destruction_observers_.push_back(observer);
        }

        void RemoveObserver(DestructionObserver* observer)
        {
            for (unsigned int i = 0; i < destruction_observers_.size(); i++) {
                if (destruction_observers_[i] == observer) {
                    std::swap(destruction_observers_[i], destruction_observers_.back());
                    destruction_observers_.pop_back();
                    return;
                }
            }
            NOTREACHED();
        }

        struct DoTexImageArguments {
            enum TexImageCommandType {
                kTexImage2D,
                kTexImage3D,
            };

            GLenum target;
            GLint level;
            GLenum internal_format;
            GLsizei width;
            GLsizei height;
            GLsizei depth;
            GLint border;
            GLenum format;
            GLenum type;
            const void* pixels;
            uint32 pixels_size;
            TexImageCommandType command_type;
        };

        bool ValidateTexImage(
            ContextState* state,
            const char* function_name,
            const DoTexImageArguments& args,
            // Pointer to TextureRef filled in if validation successful.
            // Presumes the pointer is valid.
            TextureRef** texture_ref);

        void ValidateAndDoTexImage(
            DecoderTextureState* texture_state,
            ContextState* state,
            DecoderFramebufferState* framebuffer_state,
            const char* function_name,
            const DoTexImageArguments& args);

        // TODO(kloveless): Make GetTexture* private once this is no longer called
        // from gles2_cmd_decoder.
        TextureRef* GetTextureInfoForTarget(ContextState* state, GLenum target);
        TextureRef* GetTextureInfoForTargetUnlessDefault(
            ContextState* state, GLenum target);

        bool ValidateFormatAndTypeCombination(
            ErrorState* error_state, const char* function_name,
            GLenum format, GLenum type);

        // Note that internal_format is only checked in relation to the format
        // parameter, so that this function may be used to validate texSubImage2D.
        bool ValidateTextureParameters(
            ErrorState* error_state, const char* function_name,
            GLenum format, GLenum type, GLenum internal_format, GLint level);

        // base::trace_event::MemoryDumpProvider implementation.
        bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
            base::trace_event::ProcessMemoryDump* pmd) override;

    private:
        friend class Texture;
        friend class TextureRef;

        // Helper for Initialize().
        scoped_refptr<TextureRef> CreateDefaultAndBlackTextures(
            GLenum target,
            GLuint* black_texture);

        void DoTexImage(
            DecoderTextureState* texture_state,
            ErrorState* error_state,
            DecoderFramebufferState* framebuffer_state,
            const char* function_name,
            TextureRef* texture_ref,
            const DoTexImageArguments& args);

        void StartTracking(TextureRef* texture);
        void StopTracking(TextureRef* texture);

        void UpdateSafeToRenderFrom(int delta);
        void UpdateUnclearedMips(int delta);
        void UpdateCanRenderCondition(Texture::CanRenderCondition old_condition,
            Texture::CanRenderCondition new_condition);
        void UpdateNumImages(int delta);
        void IncFramebufferStateChangeCount();

        GLenum AdjustTexFormat(GLenum format) const;

        // Helper function called by OnMemoryDump.
        void DumpTextureRef(base::trace_event::ProcessMemoryDump* pmd,
            TextureRef* ref);

        MemoryTypeTracker* GetMemTracker(GLenum texture_pool);
        scoped_ptr<MemoryTypeTracker> memory_tracker_managed_;
        scoped_ptr<MemoryTypeTracker> memory_tracker_unmanaged_;
        MemoryTracker* memory_tracker_;

        scoped_refptr<FeatureInfo> feature_info_;

        FramebufferManager* framebuffer_manager_;

        // Info for each texture in the system.
        typedef base::hash_map<GLuint, scoped_refptr<TextureRef>> TextureMap;
        TextureMap textures_;

        GLsizei max_texture_size_;
        GLsizei max_cube_map_texture_size_;
        GLsizei max_rectangle_texture_size_;
        GLsizei max_3d_texture_size_;
        GLint max_levels_;
        GLint max_cube_map_levels_;
        GLint max_3d_levels_;

        const bool use_default_textures_;

        int num_unrenderable_textures_;
        int num_unsafe_textures_;
        int num_uncleared_mips_;
        int num_images_;

        // Counts the number of Textures allocated with 'this' as its manager.
        // Allows to check no Texture will outlive this.
        unsigned int texture_count_;

        bool have_context_;

        // Black (0,0,0,1) textures for when non-renderable textures are used.
        // NOTE: There is no corresponding Texture for these textures.
        // TextureInfos are only for textures the client side can access.
        GLuint black_texture_ids_[kNumDefaultTextures];

        // The default textures for each target (texture name = 0)
        scoped_refptr<TextureRef> default_textures_[kNumDefaultTextures];

        std::vector<DestructionObserver*> destruction_observers_;

        DISALLOW_COPY_AND_ASSIGN(TextureManager);
    };

    // This class records texture upload time when in scope.
    class ScopedTextureUploadTimer {
    public:
        explicit ScopedTextureUploadTimer(DecoderTextureState* texture_state);
        ~ScopedTextureUploadTimer();

    private:
        DecoderTextureState* texture_state_;
        base::TimeTicks begin_time_;
        DISALLOW_COPY_AND_ASSIGN(ScopedTextureUploadTimer);
    };

} // namespace gles2
} // namespace gpu

#endif // GPU_COMMAND_BUFFER_SERVICE_TEXTURE_MANAGER_H_
